/////////////////////////////////////////////////////////////////////
////                                                             ////
////  WISHBONE rev.B2 compliant I2C Master controller Testbench  ////
////                                                             ////
////                                                             ////
////  Author: Richard Herveille                                  ////
////          richard@asics.ws                                   ////
////          www.asics.ws                                       ////
////                                                             ////
////  Downloaded from: http://www.opencores.org/projects/i2c/    ////
////                                                             ////
/////////////////////////////////////////////////////////////////////
////                                                             ////
//// Copyright (C) 2001 Richard Herveille                        ////
////                    richard@asics.ws                         ////
////                                                             ////
//// This source file may be used and distributed without        ////
//// restriction provided that this copyright statement is not   ////
//// removed from the file and that any derivative work contains ////
//// the original copyright notice and the associated disclaimer.////
////                                                             ////
////     THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY     ////
//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED   ////
//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS   ////
//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR      ////
//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,         ////
//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES    ////
//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE   ////
//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR        ////
//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  ////
//// LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT  ////
//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT  ////
//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE         ////
//// POSSIBILITY OF SUCH DAMAGE.                                 ////
////                                                             ////
/////////////////////////////////////////////////////////////////////

//  CVS Log
//
//  $Id: tst_bench_top.v,v 1.8 2006-09-04 09:08:51 rherveille Exp $
//
//  $Date: 2006-09-04 09:08:51 $
//  $Revision: 1.8 $
//  $Author: rherveille $
//  $Locker:  $
//  $State: Exp $
//
// Change History:
//               $Log: not supported by cvs2svn $
//               Revision 1.7  2005/02/27 09:24:18  rherveille
//               Fixed scl, sda delay.
//
//               Revision 1.6  2004/02/28 15:40:42  rherveille
//               *** empty log message ***
//
//               Revision 1.4  2003/12/05 11:04:38  rherveille
//               Added slave address configurability
//
//               Revision 1.3  2002/10/30 18:11:06  rherveille
//               Added timing tests to i2c_model.
//               Updated testbench.
//
//               Revision 1.2  2002/03/17 10:26:38  rherveille
//               Fixed some race conditions in the i2c-slave model.
//               Added debug information.
//               Added headers.
//

`include "timescale.v"

module i2c_bench_top();

	//
	// wires && regs
	//
	reg  clk;
	reg  rst;

	reg [2:0] write_addr;
	reg [2:0] read_addr;
	reg we;
	reg re;
	reg cs;
	reg cs1;
	wire inta;

	reg [7:0] dat_i, dat_o;
	wire [7:0] dat0_i;
	wire [7:0] dat1_i;


	reg [7:0] q, qq;

	wire scl, scl0_o, scl0_oen, scl1_o, scl1_oen;
	wire sda, sda0_o, sda0_oen, sda1_o, sda1_oen;

	parameter PRER_LO = 3'b000;
	parameter PRER_HI = 3'b001;
	parameter CTR     = 3'b010;
	parameter RXR     = 3'b011;
	parameter TXR     = 3'b011;
	parameter CR      = 3'b100;
	parameter SR      = 3'b100;

	parameter TXR_R   = 3'b101; // undocumented / reserved output
	parameter CR_R    = 3'b110; // undocumented / reserved output

	parameter RD      = 1'b1;
	parameter WR      = 1'b0;
	parameter SADR    = 7'b0010_000;

	event read1;
	event write1;
	event lsy;
	//
	// Module body
	//

	// generate clock
	always #5 clk = ~clk;

	// hookup wishbone_i2c_master core
	i2c_master_top i2c_top (
		.clk_i(clk),
		.rst_i(rst),
		.cs_i(cs),
		.write_adr_i(write_addr),
		.read_adr_i(read_addr),
		.dat_i(dat_o),
		.dat_o(dat0_i),
		.we_i(we),
		.re_i(re),
		.int_o(inta),

		.scl_pad_i(scl),
		.scl_pad_o(scl0_o),
		.scl_padoen_o(scl0_oen),
		.sda_pad_i(sda),
		.sda_pad_o(sda0_o),
		.sda_padoen_o(sda0_oen)
		);


	i2c_master_top i2c_top2 (
		.clk_i(clk),
		.rst_i(rst),
		.cs_i(cs1),
		.write_adr_i(write_addr),
		.read_adr_i(read_addr),
		.dat_i(dat_o),
		.dat_o(dat1_i),
		.we_i(we),
		.re_i(re),
		.int_o(inta),

		.scl_pad_i(scl),
		.scl_pad_o(scl1_o),
		.scl_padoen_o(scl1_oen),
		.sda_pad_i(sda),
		.sda_pad_o(sda1_o),
		.sda_padoen_o(sda1_oen)
		);

	// hookup i2c slave model
	i2c_slave_model #(SADR) i2c_slave ( .scl(scl),
		.sda(sda)
	);

        // create i2c lines
	delay m0_scl (scl0_oen ? 1'bz : scl0_o, scl),
	      m1_scl (scl1_oen ? 1'bz : scl1_o, scl),
	      m0_sda (sda0_oen ? 1'bz : sda0_o, sda),
	      m1_sda (sda1_oen ? 1'bz : sda1_o, sda);

	pullup p1(scl); // pullup scl line
	pullup p2(sda); // pullup sda line

	always@(*)
	begin
		if( cs & ~cs1 )
			dat_i = dat0_i;
		else
			dat_i = dat1_i;
	end

	initial
	  begin

//	      force i2c_slave.debug = 1'b1; // enable i2c_slave debug information
	      force i2c_slave.debug = 1'b0; // disable i2c_slave debug information

	      $display("\nstatus: %t Testbench started\n\n", $time);

	      // initially values
	      clk = 0;
		  we = 0;
		  re = 0;
		  cs = 0;
		  cs1 = 0;
		  q = 0;
		  qq = 0;

	      // reset system
	      rst = 1'b0; // negate reset
	      #2;
	      rst = 1'b1; // assert reset
	      repeat(2) @(posedge clk);
	      rst = 1'b0; // negate reset

	      $display("status: %t done reset", $time);

	      @(posedge clk);

	      //
	      // program core
	      //
// program internal registers write_register(PRER_LO,8'hfa);// load prescaler lo-byte
		  write_register(PRER_LO,8'd200);// load prescaler lo-byte(repeat write for test)//c8
		  write_register(PRER_HI,8'h00);// load prescaler hi-byte

		  write_register(CTR,8'hc0);	//enable core

		  write_register2(PRER_LO,8'hfa);// load prescaler lo-byte
		  write_register2(PRER_LO,8'd200);// load prescaler lo-byte(repeat write for test) d200
		  write_register2(PRER_HI,8'h00);// load prescaler hi-byte

		  write_register2(CTR,8'hc0);	//enable core

	      $display("status: %t core enabled", $time);

	      //
	      // access slave (write)
	      //

	      // drive slave address
	      write_register(TXR, {SADR,WR} ); // present slave address, set write-bit
	      write_register(CR, 8'h90 ); // set command (start, write)
	      $display("status: %t generate 'start', write cmd %0h (slave address+write)", $time, {SADR,WR} );

	      // check tip bit
		  read_register(SR,q);
	      while(q[1])
	        read_register(SR,q);// poll it until it is zero


	      $display("status: %t tip==0", $time);

	      // send memory address
	      write_register(TXR,     8'h01); // present slave's memory address
	      write_register(CR,      8'h10); // set command (write)
	      $display("status: %t write slave memory address 01", $time);

	      // check tip bit
	      read_register(SR, q);
	      while(q[1])
	           read_register(SR, q); // poll it until it is zero
	      $display("status: %t tip==0", $time);

	      // send memory contents
	      write_register(TXR,     8'ha5); // present data
	      write_register(CR,      8'h10); // set command (write)
	      $display("status: %t write data a5", $time);

		  while (scl) #1;	//insert some zero during transfer
		  force scl= 1'b0;
		  ->lsy;
		  #100000;
		  release scl;

	      // check tip bit
	      read_register(SR, q);
	      while(q[1])
	           read_register(SR, q); // poll it until it is zero
	      $display("status: %t tip==0", $time);

	      // send memory contents for next memory address (auto_inc)
	      write_register(TXR,     8'h5a); // present data
	      write_register(CR,      8'h50); // set command (stop, write)
	      $display("status: %t write next data 5a, generate 'stop'", $time);

	      // check tip bit
	      read_register(SR, q);
	      while(q[1])
	           read_register(SR, q); // poll it until it is zero
	      $display("status: %t tip==0", $time);

	      //
	      // delay
	      //
	      #100000; // wait for 100us.
	      $display("status: %t wait 100us", $time);

	      //
	      // access slave (read)
	      //

	      // drive slave address
	      write_register(TXR,{SADR,WR} ); // present slave address, set write-bit
	      write_register(CR,     8'h90 ); // set command (start, write)
	      $display("status: %t generate 'start', write cmd %0h (slave address+write)", $time, {SADR,WR} );

	      // check tip bit
	      read_register(SR, q);
	      while(q[1])
	           read_register(SR, q); // poll it until it is zero
	      $display("status: %t tip==0", $time);

	      // send memory address
	      write_register(TXR,     8'h01); // present slave's memory address
	      write_register(CR,      8'h10); // set command (write)
	      $display("status: %t write slave address 01", $time);

	      // check tip bit
	      read_register(SR, q);
	      while(q[1])
	           read_register(SR, q); // poll it until it is zero
	      $display("status: %t tip==0", $time);

	      // drive slave address
	      write_register(TXR, {SADR,RD} ); // present slave's address, set read-bit
	      write_register(CR,      8'h90 ); // set command (start, write)
	      $display("status: %t generate 'repeated start', write cmd %0h (slave address+read)", $time, {SADR,RD} );

	      // check tip bit
	      read_register(SR, q);
	      while(q[1])
	           read_register(SR, q); // poll it until it is zero
	      $display("status: %t tip==0", $time);

	      // read data from slave
	      write_register(CR,      8'h20); // set command (read, ack_read)
	      $display("status: %t read + ack", $time);

	      // check tip bit
	      read_register(SR, q);
	      while(q[1])
	           read_register(SR, q); // poll it until it is zero
	      $display("status: %t tip==0", $time);

	      // check data just received
	      read_register(RXR, qq);
		  ->lsy;
	      if(qq !== 8'ha5)
	        $display("\nERROR: Expected a5, received %x at time %t", qq, $time);
	      else
	        $display("status: %t received %x", $time, qq);

	      // read data from slave
	      write_register(CR,      8'b01101000); // set command (read, nack_read,stop)
	      $display("status: %t read + ack", $time);

	      // check tip bit
	      read_register(SR, q);
	      while(q[1])
	           read_register(SR, q); // poll it until it is zero
	      $display("status: %t tip==0", $time);

	      // check data just received
	      read_register(RXR, qq);
		  ->lsy;
	      if(qq !== 8'h5a)
	        $display("\nERROR: Expected 5a, received %x at time %t", qq, $time);
	      else
	        $display("status: %t received %x", $time, qq);

			#200000;
		  

	      // read data from slave
	     // write_register(CR,      8'h20); // set command (read, ack_read)
	     // $display("status: %t read + ack", $time);

	     // // check tip bit
	     // read_register(SR, q);
	     // while(q[1])
	     //      read_register(SR, q); // poll it until it is zero
	     // $display("status: %t tip==0", $time);

	     // // check data just received
	     // read_register(RXR, qq);
		 // ->lsy;
	     // $display("status: %t received %x from 3rd read address", $time, qq);

	     // // read data from slave
	     // write_register(CR,      8'h28); // set command (read, nack_read)
	     // $display("status: %t read + nack", $time);

	     // // check tip bit
	     // read_register(SR, q);
	     // while(q[1])
	     //      read_register(SR, q); // poll it until it is zero
	     // $display("status: %t tip==0", $time);

	     // // check data just received
	     // read_register(RXR, qq);
		 // ->lsy;
	     // $display("status: %t received %x from 4th read address", $time, qq);

	      //
	      // check invalid slave memory address
	      //

	      // drive slave address
	      write_register(TXR, {SADR,WR} ); // present slave address, set write-bit
	      write_register(CR,      8'h90 ); // set command (start, write)
	      $display("status: %t generate 'start', write cmd %0h (slave address+write). Check invalid address", $time, {SADR,WR} );

	      // check tip bit
	      read_register(SR, q);
	      while(q[1])
	           read_register(SR, q); // poll it until it is zero
	      $display("status: %t tip==0", $time);

	      // send memory address
	      write_register(TXR,     8'h10); // present slave's memory address
	      write_register(CR,      8'h10); // set command (write)
	      $display("status: %t write slave memory address 10", $time);

	      // check tip bit
	      read_register(SR, q);
	      while(q[1])
	           read_register(SR, q); // poll it until it is zero
	      $display("status: %t tip==0", $time);

	      // slave should have send NACK
	      $display("status: %t Check for nack", $time);
	      if(!q[7])
	        $display("\nERROR: Expected NACK, received ACK\n");

	      // read data from slave
	      write_register(CR,      8'h40); // set command (stop)
	      $display("status: %t generate 'stop'", $time);

	      // check tip bit
	      read_register(SR, q);
	      while(q[1])
	      	read_register(SR, q); // poll it until it is zero
	      $display("status: %t tip==0", $time);

	      #250000; // wait 250us
		  ->lsy;

		  write_register(CR,8'h01);	//clear inta flag;
		  #2000;

		  arbitration_test;
		  ->lsy;
	      $display("\n\nstatus: %t Testbench done", $time);
	      $stop;
	  end



task arbitration_test; 
  begin
      //drive slave address
      write_register(TXR,{SADR,WR});	//present slave address, set write-bit
      write_register(CR,8'h90);		//set command ( start, write )
	  write_register2(TXR,{SADR,WR});//set i2c_2;
	  write_register2(CR,8'h90);
      //check tip bit
      read_register(SR,q);
      while(q[1])
      	read_register(SR,q);	//poll it until it's zero

      //send memory address 
      write_register(TXR,8'h01); //present slave's memory address
      write_register(CR,8'h10);  //set command ( write )
	  write_register2(TXR,8'h20); //present slave's memory address
      write_register2(CR,8'h11);  //set command ( write )

      //check tip bit
      read_register(SR,q);
      while(q[1])
      	read_register(SR,q);	//poll it until it's zero
    	
      //send memory contents
      write_register(TXR,8'hEF); //present data
      write_register(CR,8'h50);  //set command ( stop, write )
      write_register2(TXR,8'hEF); //present data
      write_register2(CR,8'h50);  //set command ( stop, write )
      //check tip bit
      read_register(SR,q);
      while(q[1])
      	read_register(SR,q);	//poll it until it's zero
	  ->lsy;
	  #2000000;
  end
endtask

task write_one_byte;
	input which_i2c;
	input [7:0] memory_addr;
	  begin
	  	if(which_i2c == 0)
		 begin
		  //drive slave address
		  write_register(TXR,{SADR,WR});	//present slave address, set write-bit
		  write_register(CR,8'h90);		//set command ( start, write )
		  //check tip bit
		  read_register(SR,q);
		  while(q[1])
		  	read_register(SR,q);	//poll it until it's zero

		  //send memory address 
		  write_register(TXR,memory_addr); //present slave's memory address
		  write_register(CR,8'h10);  //set command ( write )
		  //check tip bit
		  read_register(SR,q);
		  while(q[1])
		  	read_register(SR,q);	//poll it until it's zero
			
		  //send memory contents
		  write_register(TXR,8'hEF); //present data
		  write_register(CR,8'h40);  //set command ( stop, write )
		  //check tip bit
		  read_register(SR,q);
		  while(q[1])
		  	read_register(SR,q);	//poll it until it's zero
		 end
		else if(which_i2c == 1)
		 begin
		  //drive slave address
		  write_register2(TXR,{SADR,WR});	//present slave address, set write-bit
		  write_register2(CR,8'h90);		//set command ( start, write )
		  //check tip bit
		  read_register2(SR,q);
		  while(q[1])
		  	read_register2(SR,q);	//poll it until it's zero

		  //send memory address 
		  write_register2(TXR, memory_addr); //present slave's memory address
		  write_register2(CR,8'h10);  //set command ( write )
		  //check tip bit
		  read_register2(SR,q);
		  while(q[1])
		  	read_register2(SR,q);	//poll it until it's zero
			
		  //send memory contents
		  write_register2(TXR,8'hEF); //present data
		  write_register2(CR,8'h40);  //set command ( stop, write )
		  //check tip bit
		  read_register2(SR,q);
		  while(q[1])
		  	read_register2(SR,q);	//poll it until it's zero
		 end
      end
endtask


task automatic write_register;
  input [2:0] reg_addr;
  input [7:0] reg_data;

	begin
	@ (posedge clk);
	#1;
	cs = 1;
	write_addr = reg_addr;
	we = 1;
	dat_o = reg_data;
	@ (posedge clk);
	->write1;
	#1;
	we = 0;
	write_addr = 8'b0;
	cs = 0;
	
    end
endtask

task automatic read_register;
  input  [2:0] reg_addr;
  output [7:0] data;

	begin
	 @(posedge clk);
	 #1;
	 ->read1;
	 cs =1;
	 read_addr = reg_addr;
	 re = 1;
	 @(posedge clk);
	 data = dat_i;
	 #1;
	 re = 0;
	 cs = 0;
	 read_addr = 8'b0;
	 #1;
	end
endtask

task automatic write_register2;
  input [2:0] reg_addr;
  input [7:0] reg_data;

	begin
	@ (posedge clk);
	#1;
	cs1 = 1;
	write_addr = reg_addr;
	we = 1;
	dat_o = reg_data;
	@ (posedge clk);
	->write1;
	#1;
	we = 0;
	write_addr = 8'b0;
	cs1 = 0;
    end
endtask

task automatic read_register2;
  input  [2:0] reg_addr;
  output [7:0] data;

	begin
	 @(posedge clk);
	 #1;
	 ->read1;
	 cs1 =1;
	 read_addr = reg_addr;
	 re = 1;
	 @(posedge clk);
	 data = dat_i;
	 #1;
	 re = 0;
	 cs1 = 0;
	 read_addr = 8'b0;
	 #1;
	end
endtask

//only for simulation when test state machine
reg [5*8-1:0] state_str; 
parameter [4:0] ST_IDLE  = 5'b0_0000;
parameter [4:0] ST_START = 5'b0_0001;
parameter [4:0] ST_READ  = 5'b0_0010;
parameter [4:0] ST_WRITE = 5'b0_0100;
parameter [4:0] ST_ACK   = 5'b0_1000;
parameter [4:0] ST_STOP  = 5'b1_0000;


always @(i2c_top.byte_controller.c_state)
begin
	case(i2c_top.byte_controller.c_state)
	  ST_IDLE:   state_str = "IDLE ";
	  ST_START:  state_str = "START";
	  ST_READ:   state_str = "READ ";
	  ST_WRITE:  state_str = "WRITE";
	  ST_ACK:	 state_str = " ACK ";
	  ST_STOP:	 state_str = "STOP ";
	endcase
end

	reg [7*8-1:0] bit_state_str; 
    parameter [17:0] idle    = 18'b0_0000_0000_0000_0000;
    parameter [17:0] start_a = 18'b0_0000_0000_0000_0001;
    parameter [17:0] start_b = 18'b0_0000_0000_0000_0010;
    parameter [17:0] start_c = 18'b0_0000_0000_0000_0100;
    parameter [17:0] start_d = 18'b0_0000_0000_0000_1000;
    parameter [17:0] start_e = 18'b0_0000_0000_0001_0000;
    parameter [17:0] stop_a  = 18'b0_0000_0000_0010_0000;
    parameter [17:0] stop_b  = 18'b0_0000_0000_0100_0000;
    parameter [17:0] stop_c  = 18'b0_0000_0000_1000_0000;
    parameter [17:0] stop_d  = 18'b0_0000_0001_0000_0000;
    parameter [17:0] rd_a    = 18'b0_0000_0010_0000_0000;
    parameter [17:0] rd_b    = 18'b0_0000_0100_0000_0000;
    parameter [17:0] rd_c    = 18'b0_0000_1000_0000_0000;
    parameter [17:0] rd_d    = 18'b0_0001_0000_0000_0000;
    parameter [17:0] wr_a    = 18'b0_0010_0000_0000_0000;
    parameter [17:0] wr_b    = 18'b0_0100_0000_0000_0000;
    parameter [17:0] wr_c    = 18'b0_1000_0000_0000_0000;
    parameter [17:0] wr_d    = 18'b1_0000_0000_0000_0000;



always @(i2c_top.byte_controller.bit_controller.c_state)
begin
	case(i2c_top.byte_controller.bit_controller.c_state)
    	idle   : bit_state_str = "idle   ";
    	start_a: bit_state_str = "start_a";
    	start_b: bit_state_str = "start_b";
    	start_c: bit_state_str = "start_c";
    	start_d: bit_state_str = "start_d";
    	start_e: bit_state_str = "start_e";
    	stop_a : bit_state_str = "stop_a ";
    	stop_b : bit_state_str = "stop_b ";
    	stop_c : bit_state_str = "stop_c ";
    	stop_d : bit_state_str = "stop_d ";
    	rd_a   : bit_state_str = "rd_a   ";
    	rd_b   : bit_state_str = "rd_b   ";
    	rd_c   : bit_state_str = "rd_c   ";
    	rd_d   : bit_state_str = "rd_d   ";
    	wr_a   : bit_state_str = "wr_a   ";
    	wr_b   : bit_state_str = "wr_b   ";
    	wr_c   : bit_state_str = "wr_c   ";
    	wr_d   : bit_state_str = "wr_d   ";
	endcase
end


endmodule

module delay (in, out);
  input  in;
  output out;

  assign out = in;

  specify
    (in => out) = (600,600);
  endspecify
endmodule


